FirstCandidateSelector = candidate_selector {
  id :first
  desc "Selects the first available candidate (ascending order)"
  logic { select(possibilities.first) }
}

UberCandidateSelector = candidate_selector {
  id :uber
  desc "Selects the candidate which occurs the least in total in the containing houses"
  
  logic {
    selected_candidate = possibilities.min_by do |i|
      seen_fields.inject(0) { |sum, f| f.possibilities.include?(i) ? sum + 1 : sum }
    end
    
    select(selected_candidate)
  }
}

LastCandidateSelector = candidate_selector {
  id :last
  desc "Selects the last available candidate (ascending order)"
  logic { select(possibilities.last) }
}

LeastCommonCandidateSelector = candidate_selector {
  id :least_common
  desc "Selects the candidate that occurs the least in the entire sudoku grid"
  
  logic {
    selected_candidate = possibilities.min_by do |i|
      sudoku.unsolved_fields.select { |f| f.possibilities.include?(i) }.size
    end
    
    select(selected_candidate)
  }
}

MostCommonCandidateSelector = candidate_selector {  # bad choice, even though intuitively you wouldn't think so
  id :most_common
  desc "Selects the candidate that occurs the most in the entire sudoku grid"
  
  logic {
    selected_candidate = possibilities.max_by do |i|
      sudoku.unsolved_fields.select { |f| f.possibilities.include?(i) }.size
    end
    
    select(selected_candidate)
  }
}


HiddenCandidateSelector = candidate_selector {
  id :hidden
  desc "Prefers almost hidden singles"
  
  logic {
    selected_candidate = possibilities.min_by do |p|
      # if this possibility is ALMOST alone in one of the containing houses, we can kill all the rest by guessing it
      counts = containing_houses.collect do |house| # count the number of times this possibility occurs in each of the containing houses.
        house.select { |f| f.possibilities.include?(p) }.size
      end
      
      counts.sort!
      (counts[0]* 9 + counts[1]) * 9 + counts[2] # prioritize the lowest count over the others, so 2, 6, 6 gets precedence over 4, 4, 4 (not the case with uber!)
    end
    
    select(selected_candidate)
  }
}

RandomCandidateSelector = candidate_selector {
  id :random
  desc "Selects a random candidate"
  logic { select(possibilities.random) }
}
